package homework;

import cn.hutool.core.io.FileUtil;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat V4.0，在已有Minicat基础上进一步扩展，模拟出webapps部署效果
 *
 * @Author: fanbo
 * @Date: 2021/1/25 5:23 上午
 */
public class Bootstrap {

    private Server server;

    private Mapper mapper = new Mapper();

    public static void main(String[] args) throws Exception {

        Bootstrap bootstrap = new Bootstrap();

        /** 解析server.xml，创建Server对象 */
        bootstrap.loadServerXml();

        /** 加载appBase目录下部署的项目 */
        bootstrap.loadWebApp();

        /** 启动MiniCat */
        bootstrap.start();
    }

    /**
     * 加载appBase目录下的应用
     */
    private void loadWebApp() {
        Host host = server.getService().getEngine().getHost();
        String hostName = host.getName();
        String appBase = host.getAppBase();
        File file = new File(appBase);
        if (file.isDirectory()) {
            List<File> files = FileUtil.loopFiles(file, 1, null);
            for (File webApp : files) {
                String webAppName = webApp.getName();
                // mappedContext
                mapper.getHosts().get(hostName).getContexts().put(webAppName, mapper.new MappedContext());

                // 解析webApp的web.xml文件
                for (File file1 : FileUtil.loopFiles(webApp)) {
                    if ("web.xml".equals(file1.getName())) {
                        loadWebAppWebXml(file1, webAppName, appBase, hostName);
                    }
                }
            }
        }
    }

    /**
     * 解析webApp的web.xml文件
     */
    private void loadWebAppWebXml(File file, String webAppName, String appBase, String hostName) {
        /*
          <?xml version="1.0" encoding="utf-8" ?>
<web-app>
    <servlet>
        <servlet-name>ServletDemoOne</servlet-name>
        <servlet-class>servlet.ServletDemoOne</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ServletDemoOne</servlet-name>
        <url-pattern>/demo1</url-pattern>
    </servlet-mapping>
</web-app>
        */
        try {
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(file);
            Element rootElement = document.getRootElement();
            List<Element> nodes = rootElement.selectNodes("//servlet");
            for (Element element : nodes) {
                Element servletNameElement = (Element) element.selectSingleNode("//servlet-name");
                String servletName = servletNameElement.getStringValue();
                Element servletClassElement = (Element) element.selectSingleNode("//servlet-class");
                String servletClass = servletClassElement.getStringValue();

                // 根据servlet-name的值，找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("//url-pattern").getStringValue();

                // 自定义类加载器加载外部servlet.class文件
                WebAppLoader myWebAppLoader = new WebAppLoader(appBase + "/" + webAppName);
                Class<?> aClass = myWebAppLoader.loadClass(servletClass);
                Servlet servlet = (Servlet) aClass.newInstance();
                Method doGet = servlet.getClass().getMethod("init");
                doGet.invoke(servlet);

                // mapperWrapper
                Mapper.MappedWrapper mappedWrapper = mapper.new MappedWrapper();
                mappedWrapper.setServlet(servlet);
                Map<String, Mapper.MappedContext> contexts = mapper.getHosts().get(hostName).getContexts();
                Mapper.MappedContext mappedContext = contexts.get(webAppName);
                mappedContext.getWrappers().put(urlPattern, mappedWrapper);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析server.xml，创建Server对象
     */
    private void loadServerXml() {
        /**
         * 待解析的xml内容
         * <Server>
         *     <Service>
         *         <Connector port="8080">
         *             <Engine>
         *                 <Host name="localhost" appBase="/Users/fanbo/webapps">
         *                 </Host>
         *             </Engine>
         *         </Connector>
         *
         *     </Service>
         * </Server>
         */
        try {
            // 解析server.xml
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(inputStream);
            Element serverElement = document.getRootElement();
            Element serviceElement = (Element) serverElement.selectSingleNode("//Service");
            Element connectorElement = (Element) serviceElement.selectSingleNode("//Connector");
            String port = connectorElement.attributeValue("port");
            Element engineElement = (Element) serviceElement.selectSingleNode("//Engine");
            Element hostElement = (Element) engineElement.selectSingleNode("//Host");
            String name = hostElement.attributeValue("name");
            String appBase = hostElement.attributeValue("appBase");

            // 生成Server对象
            this.server = new Server();
            server.setService(new Service());
            server.getService().setConnector(new Connector(port));
            server.getService().setEngine(new Engine(new Host(name, appBase)));
            // mappedHost
            mapper.getHosts().put(name, mapper.new MappedHost());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * Minicat启动需要进行的初始化工作
     */
    private void start() throws Exception {
        // 端口监听
        String port = server.getService().getConnector().getPort();
        ServerSocket serverSocket = new ServerSocket(Integer.valueOf(port));
        System.out.println("MiniCat start on port: " + port);


        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, server, mapper);
            threadPoolExecutor.execute(requestProcessor);
        }

    }


}
